﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HexEditor_
{
    public class FormatMark
    {
        public string DataTypeName { get; set; }
        public Int64 Offset { get; set; }
    }

    public class FormatMarkManager
    {
        public List<FormatMark> FormatMarkList { get; set; }
        
        public void Initialize()
        {
            FormatMarkList = new List<FormatMark>();
        }

        int FindMark(Int64 offset)
        {
            int count = FormatMarkList.Count;
            for (int i = 0; i < count; ++i)
            {
                if (FormatMarkList[i].Offset >= offset)
                {
                    return i;
                }
            }
            return count;
        }

        int FindMarkContainData(Int64 offset)
        {
            int count = FormatMarkList.Count;
            for (int i = 1; i < count; ++i)
            {
                if (FormatMarkList[i].Offset > offset)
                {
                    return i - 1;
                }
            }
            return count - 1;
        }

        //end为最有一个被设置byte的下一个byte索引
        public int SetFormatRange(HexEditor editor, string typename, Int64 begin, Int64 end)
        {
            if (!editor._DataTypeManager.IsExist(typename))
            {
                HexEditor.LastErrorMsg = "Data type not supported";
                return -1;
            }

            if ((end - begin) % editor._DataTypeManager.GetDataType(typename).GetBytesRequired() != 0)
            {
                HexEditor.LastErrorMsg = "Bytes to format is not n*data type length";
                return -2;
            }

            int beginIdx = FindMark(begin);
            if (beginIdx - 1 >= 0 &&
                (begin - FormatMarkList[beginIdx - 1].Offset) %
                editor._DataTypeManager.GetDataType(FormatMarkList[beginIdx - 1].DataTypeName).GetBytesRequired() != 0)
            {
                HexEditor.LastErrorMsg = "New format start pos will break format";
                return -3;
            }

            FormatMark mark = new FormatMark();
            mark.DataTypeName = typename;
            mark.Offset = begin;
            FormatMarkList.Insert(beginIdx, mark);

            int endidx = FindMark(end);
            if (endidx + 1 >= FormatMarkList.Count ||
                FormatMarkList[endidx].Offset == end)
            {
                FormatMark lastmark = beginIdx > 0 ? FormatMarkList[beginIdx - 1] : null;
                if (endidx - 1 != beginIdx)
                {
                    lastmark = FormatMarkList[endidx - 1];
                }
                if (lastmark != null)
                {
                    if (endidx + 1 < FormatMarkList.Count && 
                        (FormatMarkList[endidx + 1].Offset - end) %
                        editor._DataTypeManager.GetDataType(lastmark.DataTypeName).GetBytesRequired() != 0)
                    {
                        HexEditor.LastErrorMsg = "New format end pos will break format";
                        FormatMarkList.RemoveAt(beginIdx);
                        return -4;
                    }

                    FormatMark endmark = new FormatMark();
                    endmark.DataTypeName = lastmark.DataTypeName;
                    endmark.Offset = end;
                    FormatMarkList.Insert(endidx, endmark);
                }
            }
            FormatMarkList.RemoveRange(beginIdx + 1, endidx - beginIdx - 1);
            return 0;
        }

        public int SetFormat(HexEditor editor, string typename, Int64 begin, int count)
        {
            return SetFormatRange(editor, typename, begin, begin + count);
        }

        //判断是否为可格式化单元起点
        public bool IsFormatItemStart(Int64 offset, ref DataTypeManager dtmanager)
        {
            int idx = FindMarkContainData(offset);
            DataType dt = dtmanager.GetDataType(FormatMarkList[idx].DataTypeName);
            return (offset - FormatMarkList[idx].Offset) % dt.GetBytesRequired() == 0;
        }

        public bool IsFormatItemStart(Int64 offset, int markPos, ref DataTypeManager dtmanager)
        {
            DataType dt = dtmanager.GetDataType(FormatMarkList[markPos].DataTypeName);
            return (offset - FormatMarkList[markPos].Offset) % dt.GetBytesRequired() == 0;
        }

        public Int64 GetNextFromatItemStart(Int64 offset, ref DataTypeManager dtmanager)
        {
            int idx = FindMarkContainData(offset);
            DataType dt = dtmanager.GetDataType(FormatMarkList[idx].DataTypeName);
            Int64 n = (offset - FormatMarkList[idx].Offset) % dt.GetBytesRequired();
            if (n == 0)
            {
                return offset;
            }
            return offset + (dt.GetBytesRequired() - n);
        }
    }
}
